package test;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.LinkedList;
import java.util.List;


/*************************************************************
 * Description: 计算当前坐标是否在不规则多变形内
 * 算法名称 Ray-casting Algorithm 光线投射算法
 * https://en.wikipedia.org/wiki/Point_in_polygon#Ray_casting_algorithm
 * CreateTime: 2017/7/21
 ************************************************************/
public class RayCastingAlgorithm {


    /**
     * 判断当前坐标点,是否在不规则多边形区域内
     *
     * @param point  当前坐标点(经度,纬度)
     * @param points 不规则多边形坐标点集合
     *               从一个点引出一条射线,与多边形的任意若干条边相交,计数器初始化为0,
     *               若相交处被多边形的边从左到右切过,则计数器+1
     *               若相交处被多边形的边从右到左切过,则计数器-1
     *               最后检查计数器 如果是0,点在多边形外,如果!=0 点在多边形内
     * @return
     */
    public static Boolean isWithIn(Point point, List<Point> points) {
        //param nonezeroMode True
        Boolean nonezeroMode = Boolean.TRUE;
        int pointSize = points.size();
        //2点成线,>3点成面
        if (pointSize < 3) {
            return Boolean.FALSE;
        }
        int j = pointSize - 1;
        Boolean oddNodes = Boolean.FALSE;
        int zeroState = 0;

        for (int k = 0; k < pointSize; k++) {

            Point point_k = points.get(k);
            Point point_j = points.get(j);

            BigDecimal x = point_j.getLongitude().subtract(point_k.getLongitude());

            BigDecimal y = point.getLatitude().subtract(point_k.getLatitude());

            BigDecimal y1 = point_j.getLatitude().subtract(point_k.getLatitude());

            BigDecimal x1;

            if (y1.doubleValue() != 0.0) {
                x1 = x.multiply(y).divide(y1, 6, BigDecimal.ROUND_HALF_UP).add(point_k.getLongitude());
            } else {
                x1 = point_k.getLongitude();
            }

            if ((point_k.getLatitude().doubleValue() > point.getLatitude().doubleValue()) != (point_j.getLatitude().doubleValue() > point.getLatitude().doubleValue()) &&
                    point.getLongitude().doubleValue() < x1.doubleValue()) {

                oddNodes = Boolean.TRUE;
                if (point_k.getLatitude().doubleValue() > point_j.getLatitude().doubleValue()) {
                    zeroState++;
                } else {
                    zeroState--;
                }
            }
            j = k;
        }
        Boolean result = nonezeroMode ? zeroState != 0 : oddNodes;
        return result;
    }

    public static void main(String[] args) {

        List<Point> points = new LinkedList<Point>();
        try {
//            BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\warren\\Documents\\pythonWorkspace\\test\\区域边界坐标\\乌什县.csv"));
            BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("C:\\Users\\warren\\Documents\\pythonWorkspace\\test\\区域边界坐标\\乌什县.csv"), "gbk"));
            reader.readLine();
            String line = null;
            while ((line = reader.readLine()) != null) {
                String item[] = line.split(",");
//                String last = item[item.length-1];
                String longitude = item[1];
                String latitude = item[2];
//                System.out.println(longitude +","+ latitude);
                points.add(new Point(new BigDecimal(longitude),new BigDecimal(latitude)));
//                System.out.println(last);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        Point point = new Point(new BigDecimal(79.22457), new BigDecimal(41.21472));
        Boolean result = isWithIn(point, points);
        System.out.println(result);
    }


    /**
     * 定义Point(x,y)
     * x = longitude
     * y = latitude
     */
    public static class Point {

        /**
         * 经度
         */
        private BigDecimal longitude;

        /**
         * 维度
         */
        private BigDecimal latitude;


        public Point(BigDecimal longitude, BigDecimal latitude) {
            this.longitude = longitude;
            this.latitude = latitude;
        }

        public BigDecimal getLongitude() {
            return longitude;
        }

        public void setLongitude(BigDecimal longitude) {
            this.longitude = longitude;
        }

        public BigDecimal getLatitude() {
            return latitude;
        }

        public void setLatitude(BigDecimal latitude) {
            this.latitude = latitude;
        }
    }
}


